#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <openssl/evp.h>

#define FILE_PLAIN "ESPIA.txt"
#define FILE_CIPHER "ESPIA.aes"
#define FILE_UNCIPHER "ESPIA.decrypted"

#define BLOCK_SIZE 16
char iv[EVP_MAX_IV_LENGTH] = "my iv";

int
encrypt( char *ciphertext, char *plaintext, char *mykey )
{
	int in_len, out_len, term_len;	
	EVP_CIPHER_CTX ctx;
	
	in_len = strlen(plaintext);
	
    EVP_EncryptInit(&ctx, EVP_aes_128_cbc(), mykey, iv);
    
    EVP_EncryptUpdate(&ctx, ciphertext, &out_len, plaintext, in_len);
    
    EVP_EncryptFinal(&ctx, &ciphertext[out_len], &term_len);
    return out_len+term_len;
}

void
decrypt( char *unciphertext, char *ciphertext, int cipher_len, char *mykey )
{
	int in_len, out_len;	
	EVP_CIPHER_CTX ctx;
		
	EVP_DecryptInit(&ctx, EVP_aes_128_cbc(), mykey, iv);
	EVP_DecryptUpdate(&ctx, unciphertext, &in_len, ciphertext, cipher_len);
	printf("inlen =%d\n", in_len);
	EVP_DecryptFinal(&ctx, &unciphertext[in_len], &in_len);
	return;
}

int
aesKeyWorks( char *aeskey, int fd)
{	
	int n, error, i;
	char unciphertext[1024]={0};
	char buffer[1024];
	
	lseek( fd, 0, SEEK_SET);
	if( (n = read( fd, buffer, BLOCK_SIZE ) ) == -1 )
	{
		perror( "read error");
		return 0;
	}
	decrypt( unciphertext, buffer, n, aeskey );
	
	printf("Decrypted : [%s]\n", unciphertext);
	
	for( i= 0, error=0 ; !error && i< strlen(unciphertext) ; i++ )
		if( unciphertext[i] < 32 || unciphertext[i] > 127 )
			error = 1;
	return !error;
}

int
aesDecryptFile( int fd_uncipher, int fd_cipher, char *aeskey )
{
	unsigned char outbuf[4096];
	int olen, tlen, n;
	char inbuff[1024];
	EVP_CIPHER_CTX ctx;
	
	lseek( fd_cipher, 0, SEEK_SET);
	EVP_CIPHER_CTX_init (& ctx);
	EVP_DecryptInit (& ctx, EVP_aes_128_cbc(), aeskey, iv);
	
	bzero (& inbuff, 1024);	
	while( (n = read (fd_cipher, inbuff, 1024)) != 0 )
	{	
		printf("entre al ciclo de lectura\n");
		printf("inbuff leido=[%s]   n=%d\n", inbuff, n);
		if (n == -1)
		{
		    perror ("read error");
		    break;
	    }
		bzero (& outbuf, 4096);
		olen = 0;
		if (EVP_DecryptUpdate (& ctx, outbuf, & olen, inbuff, n) != 1)
	    {
		    printf ("error in decrypt update\n");
		    return 1;
	    }
		printf("\t UPDATE outbuf=[%s]  len=%d\n", outbuf, olen);
		
		
		if (EVP_DecryptFinal (& ctx, outbuf + olen, & tlen) != 1)
	    {
		    printf ("error in decrypt final\n");
		    return 2;
	    }
		printf("\t FINAL toutbuf=[%s]  len=%d\n", outbuf+olen, tlen);
		olen += tlen;
		printf("\t FINAL TOTAL toutbuf=[%s]  len=%d\n", outbuf, olen);
		
		if ((n = write (fd_uncipher, outbuf, olen)) == -1)
			perror ("write error");
		bzero (& inbuff, BLOCK_SIZE);		
		
	}
	EVP_CIPHER_CTX_cleanup (& ctx);
	return 0;
}

int
aesEncryptFile( int fd_cipher, int fd_plain, char *aeskey )
{	
	unsigned char outbuf[1024];
	int olen, tlen, n;
	char inbuff[BLOCK_SIZE];
	
	EVP_CIPHER_CTX ctx;
	EVP_CIPHER_CTX_init(& ctx);
	EVP_EncryptInit(&ctx, EVP_aes_128_cbc(), aeskey, iv);
	
	while( (n = read (fd_plain, inbuff, BLOCK_SIZE)) != 0 )
	{
		printf("inbuff leido=[%s]   n=%d\n", inbuff, n);
		if (n == -1)
		{
			perror ("read error");
		    return 1;
	    }
		if (EVP_EncryptUpdate (& ctx, outbuf, &olen, inbuff, n) != 1)
		{
		    printf ("error in encrypt update\n");
		    return 2;
		}
		printf("\t UPDATE outbuf=[%s]  len=%d\n", outbuf, olen);
		if (EVP_EncryptFinal (& ctx, outbuf + olen, &tlen) != 1)
		{
		   printf ("error in encrypt final\n");
		   return 3;
		}
		printf("\t FINAL toutbuf=[%s]  len=%d\n", outbuf+olen, tlen);
		olen += tlen;
		printf("\t FINAL TOTAL toutbuf=[%s]  len=%d\n", outbuf, olen);
		if ((n = write (fd_cipher, outbuf, olen)) == -1)
			perror ("write error");
		bzero (& inbuff, BLOCK_SIZE);
	}
	printf("\n================ END ENCRYPT\n\n");
	EVP_CIPHER_CTX_cleanup (& ctx);
	return 0;
}

int 
main(void) 
{
	int fd_plain, fd_cipher, fd_uncipher;
	char plaintext[1024] = "cataratas del Iguazu Misiones Argentinas,(20,2, 200)";
	char unciphertext[1024];
    char ciphertext[1024];
    char aeskey[EVP_MAX_KEY_LENGTH] = "this's my key";
    
    int cipher_len;
    
    fd_plain = open(FILE_PLAIN, O_RDONLY);
    fd_cipher = open(FILE_CIPHER, O_RDWR|O_CREAT);
    fd_uncipher = open(FILE_UNCIPHER, O_WRONLY|O_CREAT);
    
    aesEncryptFile( fd_cipher, fd_plain, aeskey );
    
    //To check what happens with a wrong key
    //strcpy( mykey, "another key, this is a wrong one");
    
    if(aesKeyWorks( aeskey, fd_cipher) )
    {
    	printf("valid key !!\n ========>>>>>\n\n");
    	aesDecryptFile( fd_uncipher, fd_cipher, aeskey );
    }
    else
    	printf("INvalid key !!\n");
    
    close(fd_plain);
    close(fd_cipher);
    close(fd_uncipher);
    return 0;
}

